package w83b.xml;

import java.io.*;
import java.net.URL;

import org.xml.sax.*;
import org.apache.xerces.parsers.DOMParser;

import w83b.xml.exception.W83bXmlErrorCodes;
import w83b.xml.exception.W83bXmlException;



/**
 * Permite validar una fuente xml contra un esquema. <br>
 * <br>
 * NOTA. Requiere <b>xercesImpl.jar </b>.
 */

public final class W83bXmlValidator implements ErrorHandler, java.io.Serializable {

	private static final long serialVersionUID = 1L;
   private DOMParser domParser;

   private boolean onlyCheckValidity;

   private StringBuffer infoMsg;

   private boolean valid;

   /** Construye un objeto de la clase. */

   public W83bXmlValidator() {
      domParser = null;
      onlyCheckValidity = true;
      infoMsg = null;
      valid = true;
   }

   /**
    * Crea el chequeador.
    * 
    * @param onlyCheckValidity
    *           Especifica si el chequeador sólo debe verificar la validez de la
    *           fuente xml. En caso afirmativo, un chequeo finalizará al
    *           encontrarse el primer error y no se reportará información acerca
    *           del mismo. En otro caso, el chequeo continuará hasta donde sea
    *           posible y se reportará información extendida de los errores y
    *           warnings encontrados.
    * @throws W83bXmlException
    *            si se produce algún error.
    */

   public void create(boolean onlyCheckValidity) throws W83bXmlException {

      String name;

      try {

         domParser = new DOMParser();

         domParser.setErrorHandler(this);

         name = "http://xml.org/sax/features/validation";
         domParser.setFeature(name, true);

         name = "http://apache.org/xml/features/validation/schema";
         domParser.setFeature(name, true);

         this.onlyCheckValidity = onlyCheckValidity;

         if (!onlyCheckValidity) {

            infoMsg = new StringBuffer(512);

            name = "http://apache.org/xml/features/continue-after-fatal-error";
            domParser.setFeature(name, true);

         }

      } 
      catch (NoClassDefFoundError exc) {
      } 
      catch (Exception exc) {
         throw new W83bXmlException(W83bXmlErrorCodes.EC_EVALUATION_EXCEPTION,
               exc);
      }

   }

   /**
    * Especifica el esquema contra el que se efectuará el chequeo.
    * 
    * @param pathname
    *           Ruta de acceso y nombre del fichero con el esquema.
    * @throws W83bXmlException
    *            si se produce algún error.
    */

   public void setSchemaFile(String pathname) throws W83bXmlException {

      String name;
      File xsdFile;

      xsdFile = new File(pathname);
      
      
      //InputStream is = this.getClass().getResourceAsStream (pathname);
      
      name = "http://apache.org/xml/properties/schema/"
            + "external-noNamespaceSchemaLocation";
      try {
         domParser.setProperty(name, xsdFile.toURI().toString());
      } catch (Exception exc) {
         throw new W83bXmlException(W83bXmlErrorCodes.EC_EVALUATION_EXCEPTION,
               exc);
      }

   }

   /**
    * Especifica el esquema contra el que se efectuará el chequeo.
    * 
    * @param pathname
    *           Ruta de acceso y nombre del fichero con el esquema.
    * @throws W83bXmlException
    *            si se produce algún error.
    */

   public void setSchemaURL(String pathname) throws W83bXmlException {

      String name;
      URL theURL;

      theURL = getClass().getResource(pathname);
      
      name = "http://apache.org/xml/properties/schema/"
         + "external-noNamespaceSchemaLocation";
      try {
         domParser.setProperty(name, theURL.toString());
      } catch (Exception exc) {
         throw new W83bXmlException(W83bXmlErrorCodes.EC_EVALUATION_EXCEPTION,
               exc);
      }
   }
   
   /**
    * Efectúa el chequeo de una fuente xml.
    * 
    * @param pathname
    *           Ruta de acceso y nombre del fichero con la fuente xml.
    * @return true si el chequeo es satisfactorio; false, en caso contrario.
    * @throws W83bXmlException
    *            si se produce algún error.
    */

   public boolean checkXmlFile(String pathname) throws W83bXmlException {

      try {

         valid = true;

         if (!onlyCheckValidity)
         {
        	 infoMsg.setLength(0);
         }

         domParser.parse(pathname);

         return valid;

      } catch (SAXParseException exc) {
         return valid;
      } catch (Exception exc) {
         throw new W83bXmlException(W83bXmlErrorCodes.EC_EVALUATION_EXCEPTION,
               exc);
      }

   }

   /**
    * Efectúa el chequeo de una fuente xml.
    * 
    * @param text
    *           Texto, codificado en UTF-8, con la fuente xml.
    * @return true si el chequeo es satisfactorio; false, en caso contrario.
    * @throws W83bXmlException
    *            si se produce algún error.
    */

   public boolean checkXmlUtf8Text(byte[] text) throws W83bXmlException {

      ByteArrayInputStream is;
      InputSource src;

      try {

         valid = true;

         if (!onlyCheckValidity)
         {
        	 infoMsg.setLength(0);
         }

         is = new ByteArrayInputStream(text);
         src = new InputSource(is);

         domParser.parse(src);

         return valid;

      } catch (SAXParseException e) {
         return valid;
      } catch (Exception exc) {
         throw new W83bXmlException(W83bXmlErrorCodes.EC_EVALUATION_EXCEPTION,
               exc);
      }

   }

   /**
    * Efectúa el chequeo de una fuente xml.
    * 
    * @param text
    *           Texto con la fuente xml.
    * @return true si el chequeo es satisfactorio; false, en caso contrario.
    * @throws W83bXmlException
    *            si se produce algún error.
    */

   public boolean checkXmlStringText(String text) throws W83bXmlException {

      StringReader r;
      InputSource src;

      try {

         valid = true;

         if (!onlyCheckValidity)
         {
        	 infoMsg.setLength(0);
         }

         r = new StringReader(text);
         src = new InputSource(r);

         domParser.parse(src);

         return valid;

      } catch (SAXParseException e) {
         return valid;
      } catch (Exception exc) {
         throw new W83bXmlException(W83bXmlErrorCodes.EC_EVALUATION_EXCEPTION,
               exc);
      }

   }

   /**
    * Devuelve la información obtenida en el chequeo de una fuente xml.
    * 
    * @return La información mencionada.
    */

   public String getCheckInfo() {

      String msg, msg1, msg2;

      if (valid)
      {
    	  msg1 = "NO ERRORS.";
      }
      else
      {
    	  msg1 = "ERRORS.";
      }

      if (infoMsg != null)
      {
    	  msg2 = infoMsg.toString();
      }
      else
      {
    	  msg2 = "";
      }

      msg = msg1 + msg2;

      return msg;

   }

   /**
    * Chequea la validez de una fuente xml contra un esquema.
    * 
    * @param xmlFilePathName
    *           Ruta de acceso y nombre del fichero con la fuente xml.
    * @param xsdFilePathName
    *           Ruta de acceso y nombre del fichero con el esquema.
    * @return true si la fuente es válida; false, en caso contrario.
    * @throws W83bXmlException
    *            si se produce algún error.
    */

   public static boolean validateXmlFileUsingXsdFile(String xmlFilePathName,
         String xsdFilePathName) throws W83bXmlException {

      boolean valid;
      W83bXmlValidator checker;

      checker = new W83bXmlValidator();

      checker.create(true);
      checker.setSchemaFile(xsdFilePathName);
      valid = checker.checkXmlFile(xmlFilePathName);

      return valid;

   }

   /**
    * Chequea la validez de una fuente xml contra un esquema.
    * 
    * @param xmlText
    *           Texto, codificado en UTF-8, con la fuente xml.
    * @param xsdFilePathName
    *           Ruta de acceso y nombre del fichero con el esquema.
    * @return true si la fuente es válida; false, en caso contrario..
    * @throws W83bXmlException
    *            si se produce algún error.
    */

   public static boolean validateXmlUtf8TextUsingXsdFile(byte[] xmlText,
         String xsdFilePathName) throws W83bXmlException {

      boolean valid;
      W83bXmlValidator checker;

      checker = new W83bXmlValidator();

      checker.create(true);
      checker.setSchemaFile(xsdFilePathName);
      valid = checker.checkXmlUtf8Text(xmlText);

      return valid;

   }

   /**
    * Chequea la validez de una fuente xml contra un esquema.
    * 
    * @param xmlText
    *           Texto con la fuente xml.
    * @param xsdFilePathName
    *           Ruta de acceso y nombre del fichero con el esquema.
    * @return true si la fuente es válida; false, en caso contrario.
    * @throws W83bXmlException
    *            si se produce algún error.
    */

   public static boolean validateXmlStringTextUsingXsdFile(String xmlText,
         String xsdFilePathName) throws W83bXmlException {

      boolean valid;
      W83bXmlValidator checker;

      checker = new W83bXmlValidator();

      checker.create(true);
      checker.setSchemaFile(xsdFilePathName);
      valid = checker.checkXmlStringText(xmlText);

      return valid;

   }

   /**
    * Método interno.
    * 
    * @param exc
    *           Ver org.xml.sax.ErrorHandler
    * @throws SAXW83bXmlException
    *            Ver org.xml.sax.ErrorHandler
    */

   public void warning(SAXParseException exc) throws SAXParseException {

      String msg;

      if (!onlyCheckValidity) {
         msg = "Warning at (" + exc.getLineNumber() + ","
               + exc.getColumnNumber() + "): " + exc.getMessage();
         infoMsg.append("\r\n\r\n").append(msg);
      }

   }

   /**
    * Método interno.
    * 
    * @param exc
    *           Ver org.xml.sax.ErrorHandler
    * @throws SAXW83bXmlException
    *            Ver org.xml.sax.ErrorHandler
    */

   public void error(SAXParseException exc) throws SAXException {

      String msg;

      valid = false;

      if (onlyCheckValidity)
      {
    	  throw exc;
      }
      else {
         msg = "Error at (" + exc.getLineNumber() + "," + exc.getColumnNumber()
               + "): " + exc.getMessage();
         infoMsg.append("\r\n\r\n").append(msg);
      }

   }

   /**
    * Método interno.
    * 
    * @param exc
    *           Ver org.xml.sax.ErrorHandler
    * @throws SAXW83bXmlException
    *            Ver org.xml.sax.ErrorHandler
    */

   public void fatalError(SAXParseException exc) throws SAXException {

      String msg;

      valid = false;

      if (onlyCheckValidity)
      {
    	  throw exc;
      }
      else {
         msg = "Fatal error at (" + exc.getLineNumber() + ","
               + exc.getColumnNumber() + "): " + exc.getMessage();
         infoMsg.append("\r\n\r\n").append(msg);
      }

   }

}